
#include "stdafx.h"

//#ifdef USE_DIRECTX

#include "canvas_d3d.h"

using namespace Blox;

Canvas_D3D::Canvas_D3D( )
	: m_driverType(D3D10_DRIVER_TYPE_NULL)
	, m_pD3DDevice(NULL)
	, m_pSwapChain(NULL)
	, m_pRenderTargetView(NULL)

	, m_pEffect_Basic(NULL)
	, m_pTechnique_DefaultRender(NULL)

	, m_pShaderWorld(NULL)
	, m_pShaderView(NULL)
	, m_pShaderProj(NULL)
{
}

//////////////////////////////////////////////////////////////////////////

bool Canvas_D3D::Init( void * pData )
{
	if( !pData )
	{
		return false;
	}

	HRESULT hResult = InitInternal( *((InitData const*)pData) );
	if( hResult==S_OK )
	{
		return true;
	}
	else
	{
		// ERROR_HANDLE!
		return false;
	}
}

HRESULT Canvas_D3D::InitInternal( InitData const & rData )
{
	HRESULT hr = S_OK;

	D3D10_DRIVER_TYPE driverTypes[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_REFERENCE };
	UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] );

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory( &sd, sizeof(sd) );
	sd.BufferCount = 1;
	sd.BufferDesc.Width = rData.uWidth;
	sd.BufferDesc.Height = rData.uHeight;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = rData.uFPS;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = rData.hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG;
#endif

	for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
	{
		m_driverType = driverTypes[driverTypeIndex];
		hr = D3D10CreateDeviceAndSwapChain( NULL, m_driverType, NULL, createDeviceFlags, D3D10_SDK_VERSION, &sd, &m_pSwapChain, &m_pD3DDevice );
		if( SUCCEEDED(hr) )
			break;
	}
	if( FAILED(hr) ) { return hr; }


	//////////////////////////////////////////////////////////////////////////
	// Get the backbuffer
	ID3D10Texture2D* pBackBuffer;
	hr = m_pSwapChain->GetBuffer( 0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer );
	if( FAILED(hr) ) { return hr; }


	//////////////////////////////////////////////////////////////////////////
	// Create a render target view
	hr = m_pD3DDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView );
	pBackBuffer->Release();
	if( FAILED(hr) ) { return hr; }

	m_pD3DDevice->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL );


	//////////////////////////////////////////////////////////////////////////
	// Setup the viewport
	D3D10_VIEWPORT vp;
	vp.Width = rData.uWidth;
	vp.Height = rData.uHeight;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	m_pD3DDevice->RSSetViewports( 1, &vp );


	//////////////////////////////////////////////////////////////////////////
	// Simple Layout
	D3D10_INPUT_ELEMENT_DESC aSimpleLayout[] = 
	{ 
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UINT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};

	//////////////////////////////////////////////////////////////////////////
	// Set up the Shaders

	// Create the Effects
	m_pEffect_Basic = new Effect( L"shaders/default.fx", *m_pD3DDevice );

	// Get Shader Vars
	m_pShaderWorld = (ID3D10EffectMatrixVariable*)m_pEffect_Basic->pEffect->GetVariableByName( "World" );
	m_pShaderView = (ID3D10EffectMatrixVariable*)m_pEffect_Basic->pEffect->GetVariableByName( "View" );
	m_pShaderProj = (ID3D10EffectMatrixVariable*)m_pEffect_Basic->pEffect->GetVariableByName( "Projection" );
		
	// Create the Techniques (InputLayout per Technique)
	m_pTechnique_DefaultRender = new Technique( "Render", *m_pEffect_Basic, *m_pD3DDevice, &aSimpleLayout[0], 2 );

	//////////////////////////////////////////////////////////////////////////
	// Set up the Buffers.
	hr = m_TriBuffer.Init( *m_pD3DDevice, sizeof(Blox::InputVert), 3*rData.uTriBufferSize, sizeof(u32), 3*rData.uTriBufferSize, DXGI_FORMAT_R32_UINT, D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	if( FAILED(hr) ) { return hr; }
	hr = m_LineBuffer.Init( *m_pD3DDevice, sizeof(Blox::InputVert), 2*rData.uLineBufferSize, sizeof(u32), 2*rData.uLineBufferSize, DXGI_FORMAT_R32_UINT, D3D10_PRIMITIVE_TOPOLOGY_LINELIST );
	if( FAILED(hr) ) { return hr; }
	hr = m_ModelBuffer.Init( *m_pD3DDevice, sizeof(Blox::InputVert), rData.uModelBufferSize, sizeof(u32), rData.uModelBufferSize, DXGI_FORMAT_R32_UINT, D3D10_PRIMITIVE_TOPOLOGY_LINELIST );
	if( FAILED(hr) ) { return hr; }


	//////////////////////////////////////////////////////////////////////////
	// Initialize the world matrix
	D3DXMATRIX mWorld;
	D3DXMatrixIdentity( &mWorld );
	m_pShaderWorld->SetMatrix( ( float* )&mWorld );

	D3DXMATRIX mView;
	D3DXVECTOR3 Eye( 0.0f, 0.0f, -5.0f );
	D3DXVECTOR3 At( 0.0f, 0.0f, 0.0f );
	D3DXVECTOR3 Up( 0.0f, 1.0f, 0.0f );
	D3DXMatrixLookAtLH( &mView, &Eye, &At, &Up );
	m_pShaderView->SetMatrix( ( float* )&mView );

	// Initialize the projection matrix
	D3DXMATRIX mProj;
	D3DXMatrixPerspectiveFovLH( &mProj, ( float )D3DX_PI * 0.5f, 640.0f/480.0f, 0.1f, 100.0f );
	m_pShaderProj->SetMatrix( ( float* )&mProj );



	return S_OK;
}

void Canvas_D3D::Clean( )
{
	//////////////////////////////////////////////////////////////////////////
	// Clear the D3D Device.
	if( m_pD3DDevice )
	{
		m_pD3DDevice->ClearState();
	}

	//////////////////////////////////////////////////////////////////////////
	// Release the rest!
	

	if( m_pTechnique_DefaultRender )
	{
		delete m_pTechnique_DefaultRender;
		m_pTechnique_DefaultRender = NULL;
	}

	if( m_pEffect_Basic )
	{
		delete m_pEffect_Basic;
		m_pEffect_Basic = NULL;
	}

	m_LineBuffer.Clean();
	m_TriBuffer.Clean();
	
	if( m_pRenderTargetView )
	{
		m_pRenderTargetView->Release();
		m_pRenderTargetView = NULL;
	}

	if( m_pSwapChain )
	{
		m_pSwapChain->Release();
		m_pSwapChain = NULL;
	}

	if( m_pD3DDevice )
	{
		m_pD3DDevice->Release();
		m_pD3DDevice = NULL;
	}
}

//////////////////////////////////////////////////////////////////////////

void Canvas_D3D::Render( RenderPacket const & rPacket )
{
	if( m_pD3DDevice && m_pSwapChain && m_pRenderTargetView )
	{
		// Clear the back buffer
		m_pD3DDevice->ClearRenderTargetView( m_pRenderTargetView, ColF_RGBA( rPacket.GetClearCol() ).AsFloatArray() );
		
		// Update variables that change once per frame
		m_pShaderView->SetMatrix( ( float* )&rPacket.GetCamera().GetTransform().f[0] );
		m_pShaderProj->SetMatrix( ( float* )&rPacket.GetCamera().GetProjection().f[0] );

		// Render
		m_pTechnique_DefaultRender->PreRender( *m_pD3DDevice, m_LineBuffer );
		m_pTechnique_DefaultRender->Render( *m_pD3DDevice, m_LineBuffer );
		m_LineBuffer.Flush();

		m_pTechnique_DefaultRender->PreRender( *m_pD3DDevice, m_TriBuffer );
		m_pTechnique_DefaultRender->Render( *m_pD3DDevice, m_TriBuffer );
		m_TriBuffer.Flush();

		// Present our back buffer to our front buffer
		m_pSwapChain->Present( 0, 0 );

	}
}

void Canvas_D3D::DrawTri( Vec const & v0, Vec const & v1, Vec const & v2, u32 c0, u32 c1, u32 c2 )
{
	Blox::InputVert i0( v0, c0 );
	Blox::InputVert i1( v1, c1 );
	Blox::InputVert i2( v2, c2 );
	m_TriBuffer.AddVert( &i0 );
	m_TriBuffer.AddVert( &i1 );
	m_TriBuffer.AddVert( &i2 );
}

void Canvas_D3D::DrawLine( Vec const & v0, Vec const & v1, u32 c0, u32 c1 )
{
	Blox::InputVert i0( v0, c0 );
	Blox::InputVert i1( v1, c1 );
	m_LineBuffer.AddVert( &i0 );
	m_LineBuffer.AddVert( &i1 );
}

void Canvas_D3D::DrawLine( Blox::InputVert const & v0, Blox::InputVert const & v1 )
{
	m_LineBuffer.AddVert( (void*)&v0 );
	m_LineBuffer.AddVert( (void*)&v1 );
}

//////////////////////////////////////////////////////////////////////////

//#endif //USE_DIRECTX
